/*
 *  ETA/OS - Python VM
 *  Copyright (C) 2017   Michel Megens <dev@bietje.net>
 *  Copyright (C) 2017   Dean Hall
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#undef __FILE_ID__
#define __FILE_ID__ 0x05

/**
 * \file
 * \brief VM Globals
 *
 * VM globals operations.
 * PyMite's global struct def and initial values.
 */

#include <etaos/python.h>

extern unsigned char const *stdlib_img;

static uint8_t const *bistr = (uint8_t const *)"__bi";

/** Most PyMite globals all in one convenient place */
volatile PmVmGlobal_t gVmGlobal;

PmReturn_t global_init(void)
{
	PmReturn_t retval;
	uint8_t *codestr = (uint8_t *) "code";
	uint8_t *pchunk;
	pPmObj_t pobj;
#ifdef HAVE_CLASSES
	uint8_t const *initstr = (uint8_t const *)"__init__";
#endif				/* HAVE_CLASSES */
#ifdef HAVE_GENERATORS
	uint8_t const *genstr = (uint8_t const *)"Generator";
	uint8_t const *nextstr = (uint8_t const *)"next";
#endif				/* HAVE_GENERATORS */
#ifdef HAVE_ASSERT
	uint8_t const *exnstr = (uint8_t const *)"Exception";
#endif				/* HAVE_ASSERT */
#ifdef HAVE_BYTEARRAY
	uint8_t const *pbastr = (uint8_t const *)"bytearray";
#endif				/* HAVE_BYTEARRAY */
	uint8_t const *pmdstr = (uint8_t const *)"__md";

	/* Clear the global struct */
	sli_memset((uint8_t *) & gVmGlobal, '\0', sizeof(PmVmGlobal_t));

	/* Set the PyMite release num (for debug and post mortem) */
	gVmGlobal.errVmRelease = PM_RELEASE;

	/* Init zero */
	retval = heap_getChunk(sizeof(PmInt_t), &pchunk);
	PM_RETURN_IF_ERROR(retval);
	pobj = (pPmObj_t) pchunk;
	OBJ_SET_TYPE(pobj, OBJ_TYPE_INT);
	((pPmInt_t) pobj)->val = (int32_t) 0;
	gVmGlobal.pzero = (pPmInt_t) pobj;

	/* Init one */
	retval = heap_getChunk(sizeof(PmInt_t), &pchunk);
	PM_RETURN_IF_ERROR(retval);
	pobj = (pPmObj_t) pchunk;
	OBJ_SET_TYPE(pobj, OBJ_TYPE_INT);
	((pPmInt_t) pobj)->val = (int32_t) 1;
	gVmGlobal.pone = (pPmInt_t) pobj;

	/* Init negone */
	retval = heap_getChunk(sizeof(PmInt_t), &pchunk);
	PM_RETURN_IF_ERROR(retval);
	pobj = (pPmObj_t) pchunk;
	OBJ_SET_TYPE(pobj, OBJ_TYPE_INT);
	((pPmInt_t) pobj)->val = (int32_t) - 1;
	gVmGlobal.pnegone = (pPmInt_t) pobj;

	/* Init False */
	retval = heap_getChunk(sizeof(PmBoolean_t), &pchunk);
	PM_RETURN_IF_ERROR(retval);
	pobj = (pPmObj_t) pchunk;
	OBJ_SET_TYPE(pobj, OBJ_TYPE_BOOL);
	((pPmBoolean_t) pobj)->val = (int32_t) C_FALSE;
	gVmGlobal.pfalse = (pPmInt_t) pobj;

	/* Init True */
	retval = heap_getChunk(sizeof(PmBoolean_t), &pchunk);
	PM_RETURN_IF_ERROR(retval);
	pobj = (pPmObj_t) pchunk;
	OBJ_SET_TYPE(pobj, OBJ_TYPE_BOOL);
	((pPmBoolean_t) pobj)->val = (int32_t) C_TRUE;
	gVmGlobal.ptrue = (pPmInt_t) pobj;

	/* Init None */
	retval = heap_getChunk(sizeof(PmObj_t), &pchunk);
	PM_RETURN_IF_ERROR(retval);
	pobj = (pPmObj_t) pchunk;
	OBJ_SET_TYPE(pobj, OBJ_TYPE_NON);
	gVmGlobal.pnone = pobj;

	/* Init "code" string obj */
	retval = string_new((uint8_t const **)&codestr, &pobj);
	PM_RETURN_IF_ERROR(retval);
	gVmGlobal.pcodeStr = (pPmString_t) pobj;

#ifdef HAVE_CLASSES
	/* Init "__init__" string obj */
	retval = string_new((uint8_t const **)&initstr, &pobj);
	PM_RETURN_IF_ERROR(retval);
	gVmGlobal.pinitStr = (pPmString_t) pobj;
#endif				/* HAVE_CLASSES */

#ifdef HAVE_GENERATORS
	/* Init "Generator" string obj */
	retval = string_new((uint8_t const **)&genstr, &pobj);
	PM_RETURN_IF_ERROR(retval);
	gVmGlobal.pgenStr = (pPmString_t) pobj;

	/* Init "next" string obj */
	retval = string_new((uint8_t const **)&nextstr, &pobj);
	PM_RETURN_IF_ERROR(retval);
	gVmGlobal.pnextStr = (pPmString_t) pobj;
#endif				/* HAVE_GENERATORS */

#ifdef HAVE_ASSERT
	/* Init "Exception" string obj */
	retval = string_new((uint8_t const **)&exnstr, &pobj);
	PM_RETURN_IF_ERROR(retval);
	gVmGlobal.pexnStr = (pPmString_t) pobj;
#endif				/* HAVE_ASSERT */

#ifdef HAVE_BYTEARRAY
	/* Init "bytearray" string obj */
	retval = string_new((uint8_t const **)&pbastr, &pobj);
	PM_RETURN_IF_ERROR(retval);
	gVmGlobal.pbaStr = (pPmString_t) pobj;
#endif				/* HAVE_BYTEARRAY */

	/* Init "__md" string obj */
	retval = string_new((uint8_t const **)&pmdstr, &pobj);
	PM_RETURN_IF_ERROR(retval);
	gVmGlobal.pmdStr = (pPmString_t) pobj;

	/* Init empty builtins */
	gVmGlobal.builtins = C_NULL;

	/* Init native frame */
	gVmGlobal.nativeframe.od = sizeof(PmNativeFrame_t);
	OBJ_SET_TYPE(&gVmGlobal.nativeframe, OBJ_TYPE_NFM);
	gVmGlobal.nativeframe.nf_func = C_NULL;
	gVmGlobal.nativeframe.nf_stack = C_NULL;
	gVmGlobal.nativeframe.nf_active = C_FALSE;
	gVmGlobal.nativeframe.nf_numlocals = 0;

	/* Create empty threadList */
	retval = list_new(&pobj);
	gVmGlobal.threadList = (pPmList_t) pobj;

	/* Init the PmImgPaths with std image info */
	gVmGlobal.imgPaths.memspace[0] = MEMSPACE_PROG;
	gVmGlobal.imgPaths.pimg[0] = (uint8_t *) & stdlib_img;
	gVmGlobal.imgPaths.pathcount = 1;

#ifdef HAVE_PRINT
	gVmGlobal.needSoftSpace = C_FALSE;
	gVmGlobal.somethingPrinted = C_FALSE;
#endif				/* HAVE_PRINT */

	return retval;
}

PmReturn_t global_setBuiltins(pPmFunc_t pmod)
{
	PmReturn_t retval = PM_RET_OK;
	pPmObj_t pkey = C_NULL;
	uint8_t const *pbistr = bistr;
	uint8_t objid;

	if (PM_PBUILTINS == C_NULL) {
		/* Need to load builtins first */
		global_loadBuiltins();
	}

	/* Put builtins module in the module's attrs dict */
	retval = string_new(&pbistr, &pkey);
	PM_RETURN_IF_ERROR(retval);

	heap_gcPushTempRoot(pkey, &objid);
	retval = dict_setItem((pPmObj_t) pmod->f_attrs, pkey, PM_PBUILTINS);
	heap_gcPopTempRoot(objid);

	return retval;
}

PmReturn_t global_loadBuiltins(void)
{
	PmReturn_t retval = PM_RET_OK;
	pPmObj_t pkey = C_NULL;
	uint8_t const *nonestr = (uint8_t const *)"None";
	uint8_t const *falsestr = (uint8_t const *)"False";
	uint8_t const *truestr = (uint8_t const *)"True";
	pPmObj_t pstr = C_NULL;
	pPmObj_t pbimod;
	uint8_t const *pbistr = bistr;

	/* Import the builtins */
	retval = string_new(&pbistr, &pstr);
	PM_RETURN_IF_ERROR(retval);
	retval = mod_import(pstr, &pbimod);
	PM_RETURN_IF_ERROR(retval);

	/* Must interpret builtins' root code to set the attrs */
	C_ASSERT(gVmGlobal.threadList->length == 0);
	interp_addThread((pPmFunc_t) pbimod);
	retval = interpret(INTERP_RETURN_ON_NO_THREADS);
	PM_RETURN_IF_ERROR(retval);

	/* Builtins points to the builtins module's attrs dict */
	gVmGlobal.builtins = ((pPmFunc_t) pbimod)->f_attrs;

	/* Set None manually */
	retval = string_new(&nonestr, &pkey);
	PM_RETURN_IF_ERROR(retval);
	retval = dict_setItem(PM_PBUILTINS, pkey, PM_NONE);
	PM_RETURN_IF_ERROR(retval);

	/* Set False manually */
	retval = string_new(&falsestr, &pkey);
	PM_RETURN_IF_ERROR(retval);
	retval = dict_setItem(PM_PBUILTINS, pkey, PM_FALSE);
	PM_RETURN_IF_ERROR(retval);

	/* Set True manually */
	retval = string_new(&truestr, &pkey);
	PM_RETURN_IF_ERROR(retval);
	retval = dict_setItem(PM_PBUILTINS, pkey, PM_TRUE);
	PM_RETURN_IF_ERROR(retval);

	/* Deallocate builtins module */
	retval = heap_freeChunk((pPmObj_t) pbimod);

	return retval;
}
